package com.swotxu.oldwebproject.session.handler;

import com.swotxu.oldwebproject.session.RedisSession;
import com.swotxu.oldwebproject.session.listener.session.HttpSessionMethodListener;
import com.swotxu.oldwebproject.session.listener.session.RedisSessionAttributeListener;
import com.swotxu.oldwebproject.session.listener.session.RedisSessionEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.http.HttpSession;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import java.util.List;

/**
 * redisSession 代理类
 * 代理 redisSession 对象，拦截其执行流程
 *
 * @Date: 2020/1/10 14:24
 * @Author: xu.hai
 */
public class RedisSessionHandler implements InvocationHandler {
    private static final Logger LOGGER = LoggerFactory.getLogger(RedisSessionHandler.class);
    private RedisSession session;
    // 事件监听集
    private static List<HttpSessionMethodListener> methodListeners;
    private static List<String> excludeMethod;

    static {
        // 注册事件监听
        methodListeners = Arrays.asList(new RedisSessionAttributeListener());
        // 初始化排除方法
        excludeMethod = Arrays.asList(RedisSessionAttributeListener.METHOD_ISNEW
                , RedisSessionAttributeListener.METHOD_INVALIDATE
                , RedisSessionAttributeListener.METHOD_ISINVALID);
    }

    private RedisSessionHandler(RedisSession session) {
        this.session = session;
    }

    public static HttpSession getInstance(RedisSession session) {
        RedisSessionHandler handler = new RedisSessionHandler(session);
        return (HttpSession) Proxy.newProxyInstance(session.getClass()
                .getClassLoader(), session.getClass().getInterfaces(), handler);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        RedisSession redisSession = (RedisSession) session;
        // 创建事件源
        RedisSessionEvent event = new RedisSessionEvent(redisSession, proxy, method, args);
        // 标识符，当前 Session 是否无效，即 Redis中无此 SessionId， true-无效
        boolean isInvalid = true;
        // 若调用 setAttribute 方法，需校验是否有效
        if (RedisSessionAttributeListener.METHOD_SETATTRIBUTE.equals(method.getName())
                && (isInvalid = redisSession.isInvalid())) {
            LOGGER.warn("[{}] session is invalid!", redisSession.getId());
            return null;
        }

        // 执行方法前监听
        processMethodBeforListeners(event);
        if (event.isInterrupte()) {
            return event.getResult();
        }
        // 执行目标方法
        event.setResult(method.invoke(redisSession, args));

        // 执行方法后监听
        processMethodAfterListeners(event);
        if (event.isInterrupte()) {
            return event.getResult();
        }

        // 当代理 session 执行非 invalidate 方法时，刷新 redis 过期时间
        if (!isInvalid && !excludeMethod.contains(method.getName())) {
            refreshSession(redisSession);
        }
        // 返回结果
        return event.getResult();
    }

    /**
     * 执行方法前监听
     * @param event
     */
    private void processMethodBeforListeners(RedisSessionEvent event){
        LOGGER.debug("process RedisSession method Befor Listeners!");
        for (HttpSessionMethodListener listener : methodListeners) {
            listener.executeBefor(event);
        }
    }

    /**
     * 执行方法前监听
     * @param event
     */
    private void processMethodAfterListeners(RedisSessionEvent event){
        LOGGER.debug("process RedisSession method After Listeners!");
        for (HttpSessionMethodListener listener : methodListeners) {
            listener.executeAfter(event);
        }
    }

    /**
     * 刷新 session 过期时间
     * @param redisSession
     */
    private void refreshSession(RedisSession redisSession){
        LOGGER.debug("refresh session!");
        redisSession.refresh();
        redisSession.setLastAccessedTime(System.currentTimeMillis());
    }
}
